/*
 * Copyright 2010, 2011, 2012 mapsforge.org
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.mapsforge.android.maps.overlay;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.mapsforge.android.maps.kml.KMLBinding;
import org.mapsforge.android.maps.kml.KMLGoogle22Binding;
import org.mapsforge.android.maps.kml.KMLOpenGis22Binding;

import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;

/**
 * A KMLLayer is in fact a list containing the different kind of overlays represented in a KML file. This class also
 * provides methods to load KML files and parse them in the aforementioned overlays.
 */
public class KMLLayer implements List<Overlay>, RandomAccess {
	private static final Logger LOGGER = Logger.getLogger(KMLLayer.class.getName());
	private static final int INITIAL_CAPACITY = 2;

	private final List<Overlay> list;

	private Drawable defaultItemMarker = null;
	private Paint defaultPolyPaintFill = null;
	private Paint defaultPolyPaintOutline = null;
	private Paint defaultLinePaintFill = null;
	private Paint defaultLinePaintOutline = null;

	private KMLBinding binding;
	private String backtrackURL;

	/**
	 * Constructs a new empty KMLOverlay.
	 */
	public KMLLayer() {
		super();
		this.list = Collections.synchronizedList(new ArrayList<Overlay>(INITIAL_CAPACITY));

		// create the default paint objects for overlay ways
		Paint wayDefaultPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
		wayDefaultPaintFill.setStyle(Paint.Style.FILL);
		wayDefaultPaintFill.setColor(Color.CYAN);
		wayDefaultPaintFill.setAlpha(192);

		Paint wayDefaultPaintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
		wayDefaultPaintOutline.setStyle(Paint.Style.STROKE);
		wayDefaultPaintOutline.setColor(Color.BLUE);
		wayDefaultPaintOutline.setAlpha(128);
		wayDefaultPaintOutline.setStrokeWidth(7);
		wayDefaultPaintOutline.setStrokeJoin(Paint.Join.ROUND);

		this.defaultPolyPaintFill = wayDefaultPaintFill;
		this.defaultPolyPaintOutline = wayDefaultPaintOutline;

		wayDefaultPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
		wayDefaultPaintFill.setStyle(Paint.Style.STROKE);
		wayDefaultPaintFill.setColor(Color.RED);
		wayDefaultPaintFill.setAlpha(192);
		wayDefaultPaintFill.setStrokeWidth(7);
		wayDefaultPaintFill.setStrokeJoin(Paint.Join.ROUND);

		wayDefaultPaintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
		wayDefaultPaintOutline.setStyle(Paint.Style.STROKE);
		wayDefaultPaintOutline.setColor(Color.RED);
		wayDefaultPaintOutline.setAlpha(128);
		wayDefaultPaintOutline.setStrokeWidth(7);
		wayDefaultPaintOutline.setStrokeJoin(Paint.Join.ROUND);

		this.defaultLinePaintFill = wayDefaultPaintFill;
		this.defaultLinePaintOutline = wayDefaultPaintOutline;

		this.setBacktrackURL("");
	}

	/**
	 * Constructs a new KMLOverlay parsing the given kml file.
	 * 
	 * @param kmlFile
	 *            is a file with a valid kml
	 * @param marker
	 *            the default drawable to mark items on the map (used to mark kml points)
	 * @param paintFill
	 *            the default paint fill to print the polygons and the lines
	 * @param paintOutline
	 *            the default paint outline to print the polygons and the lines
	 * @param backtrackURL
	 *            the base intent URL to get back to the caller Activity (if needed)
	 */
	public KMLLayer(File kmlFile, Drawable marker, Paint paintFill, Paint paintOutline, String backtrackURL) {
		super();
		this.list = Collections.synchronizedList(new ArrayList<Overlay>(INITIAL_CAPACITY));

		this.defaultItemMarker = marker;
		this.defaultPolyPaintFill = paintFill;
		this.defaultPolyPaintOutline = paintOutline;
		this.defaultLinePaintFill = paintFill;
		this.defaultLinePaintOutline = paintOutline;

		this.backtrackURL = backtrackURL;

		// Alan parsing project the file
		this.setKML(kmlFile);
	}

	/**
	 * Call the binding to unmarshal the layer object this is somewhat a reverse operation to setKML
	 * 
	 * @return a string containing the layer marshaled as a KML or null if some JiBXException occurred.
	 */
	public String getKML() {
		String result = null;

		if (this.list != null) {
			if (this.binding == null) {
				// The current layer probably has not been generated from a kml file
				// we need to create a binding
				try {
					KMLBinding ob = new KMLOpenGis22Binding(this.defaultItemMarker, this.defaultPolyPaintFill,
							this.defaultPolyPaintOutline, this.defaultLinePaintFill, this.defaultLinePaintOutline);
					this.binding = ob;

				} catch (Exception e) {
					LOGGER.log(Level.SEVERE, "Could not create a JiBX binding.", e);
				}
			}
			result = this.binding.composeKML(this.list);
		}

		return result;
	}

	/**
	 * Reads a KML file and tries to print some of its features into the Overlay List of the KMLOverlay using the
	 * default Marker, paintFill and paintOutline.
	 * 
	 * @param kmlFile
	 *            the kml file that will be parsed.
	 */
	public void setKML(File kmlFile) {
		try {
			KMLBinding gb = new KMLGoogle22Binding(this.defaultItemMarker, this.defaultPolyPaintFill,
					this.defaultPolyPaintOutline, this.defaultLinePaintFill, this.defaultLinePaintOutline);
			KMLBinding ob = new KMLOpenGis22Binding(this.defaultItemMarker, this.defaultPolyPaintFill,
					this.defaultPolyPaintOutline, this.defaultLinePaintFill, this.defaultLinePaintOutline);

			if (gb.isValidKML(kmlFile)) {
				this.binding = gb;
				LOGGER.log(Level.INFO, "Binded with KMLGoogle22.");
			} else if (ob.isValidKML(kmlFile)) {
				this.binding = ob;
				LOGGER.log(Level.INFO, "Binded with KMLOpenGis22.");
			} else {
				throw new Exception("No Binding defined.");
			}

			this.list.addAll(this.binding.parseKML(kmlFile, this.backtrackURL));

		} catch (Exception e) {
			LOGGER.log(Level.SEVERE, "Could not add or parse the kml with JiBX.", e);
		}
	}

	@Override
	public boolean add(Overlay arg0) {
		return this.list.add(arg0);
	}

	@Override
	public void add(int arg0, Overlay arg1) {
		this.list.add(arg0, arg1);
	}

	@Override
	public boolean addAll(Collection<? extends Overlay> arg0) {
		return this.list.addAll(arg0);
	}

	@Override
	public boolean addAll(int arg0, Collection<? extends Overlay> arg1) {
		return this.list.addAll(arg0, arg1);
	}

	@Override
	public void clear() {
		this.list.clear();
	}

	@Override
	public boolean contains(Object arg0) {
		return this.list.contains(arg0);
	}

	@Override
	public boolean containsAll(Collection<?> arg0) {
		return this.list.containsAll(arg0);
	}

	@Override
	public Overlay get(int arg0) {
		return this.list.get(arg0);
	}

	@Override
	public int indexOf(Object arg0) {
		return this.list.indexOf(arg0);
	}

	@Override
	public boolean isEmpty() {
		return this.list.isEmpty();
	}

	@Override
	public Iterator<Overlay> iterator() {
		return this.list.iterator();
	}

	@Override
	public int lastIndexOf(Object arg0) {
		return this.list.lastIndexOf(arg0);
	}

	@Override
	public ListIterator<Overlay> listIterator() {
		return this.list.listIterator();
	}

	@Override
	public ListIterator<Overlay> listIterator(int arg0) {
		return this.list.listIterator(arg0);
	}

	@Override
	public boolean remove(Object arg0) {
		return this.list.remove(arg0);
	}

	@Override
	public Overlay remove(int arg0) {
		return this.list.remove(arg0);
	}

	@Override
	public boolean removeAll(Collection<?> arg0) {
		return this.list.removeAll(arg0);
	}

	@Override
	public boolean retainAll(Collection<?> arg0) {
		return this.list.retainAll(arg0);
	}

	@Override
	public Overlay set(int arg0, Overlay arg1) {
		return this.list.set(arg0, arg1);
	}

	@Override
	public int size() {
		return this.list.size();
	}

	@Override
	public List<Overlay> subList(int arg0, int arg1) {
		return this.list.subList(arg0, arg1);
	}

	@Override
	public Object[] toArray() {
		return this.list.toArray();
	}

	@Override
	public <T> T[] toArray(T[] arg0) {
		return this.list.toArray(arg0);
	}

	/**
	 * gets the Default Marker that will be used to mark the kml Placemarks with geometry point which are represented as
	 * ItemizedOverlays.
	 * 
	 * @return a Drawable the Drawable used to mark items
	 */
	public Drawable getDefaultItemMarker() {
		return this.defaultItemMarker;
	}

	/**
	 * sets the Default Marker that will be used to mark the kml Placemarks with geometry point which are represented as
	 * ItemizedOverlays. This default marker may be null, but it's recommended to set it.
	 * 
	 * @param defaultMarker
	 *            this is the Drawable used to mark items
	 */
	public void setDefaultItemMarker(Drawable defaultMarker) {
		this.defaultItemMarker = defaultMarker;
	}

	/**
	 * Get the default paint fill for the polygons.
	 * 
	 * @return the default Paint used to paint the polygons
	 */
	public Paint getDefaultPolyPaintFill() {
		return this.defaultPolyPaintFill;
	}

	/**
	 * Set the default paint fill for the polygons.
	 * 
	 * @param defaultPolyPaintFill
	 *            this is a Paint representing the filling style of the polygons
	 */
	public void setDefaultPolyPaintFill(Paint defaultPolyPaintFill) {
		this.defaultPolyPaintFill = defaultPolyPaintFill;
	}

	/**
	 * Get the default paint outline for the polygons.
	 * 
	 * @return the default paint outline for the polygons.
	 */
	public Paint getDefaultPolyPaintOutline() {
		return this.defaultPolyPaintOutline;
	}

	/**
	 * Set the default paint outline for the polygons.
	 * 
	 * @param defaultPolyPaintOutline
	 *            the Paint representing the outline style of the kml polygons
	 */
	public void setDefaultPolyPaintOutline(Paint defaultPolyPaintOutline) {
		this.defaultPolyPaintOutline = defaultPolyPaintOutline;
	}

	/**
	 * Get the default paint fill for the lines.
	 * 
	 * @return the default paint fill for the lines.
	 */
	public Paint getDefaultLinePaintFill() {
		return this.defaultLinePaintFill;
	}

	/**
	 * Get the default paint fill for the lines.
	 * 
	 * @param defaultLinePaintFill
	 *            the Paint representing the fill style of the kml polygons
	 */
	public void setDefaultLinePaintFill(Paint defaultLinePaintFill) {
		this.defaultLinePaintFill = defaultLinePaintFill;
	}

	/**
	 * Get the default paint outline for the lines.
	 * 
	 * @return the default paint outline for the lines.
	 */
	public Paint getDefaultLinePaintOutline() {
		return this.defaultLinePaintOutline;
	}

	/**
	 * Get the default paint outline for the lines.
	 * 
	 * @param defaultLinePaintOutline
	 *            the Paint representing the outline style of the kml lines.
	 */
	public void setDefaultLinePaintOutline(Paint defaultLinePaintOutline) {
		this.defaultLinePaintOutline = defaultLinePaintOutline;
	}

	/**
	 * @return a URL intended to be stored in the overlays and when an onTap event or such occurs, it will be used
	 *         combined with the stored id to call, with an intent, the origin activity of the kml. It can be null and
	 *         nothing will happen.
	 */
	public String getBacktrackURL() {
		return this.backtrackURL;
	}

	/**
	 * @param backtrackURL
	 *            this parameter is a URL intended to be stored in the overlays and when an onTap event or such occurs,
	 *            it will be used combined with the stored id to call, with an intent, the origin activity of the kml.
	 *            It can be null and nothing will happen.
	 */
	public void setBacktrackURL(String backtrackURL) {
		this.backtrackURL = backtrackURL;
	}
}
